package pe.com.bbva.ge.utileria.drools;

import java.io.IOException;
import java.io.Reader;
import java.io.Serializable;
import java.io.StringReader;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;

import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.drools.KnowledgeBase;
import org.drools.KnowledgeBaseFactory;
import org.drools.builder.KnowledgeBuilder;
import org.drools.builder.KnowledgeBuilderFactory;
import org.drools.builder.ResourceType;
import org.drools.definition.KnowledgePackage;
import org.drools.io.Resource;
import org.drools.io.ResourceFactory;
import org.drools.runtime.StatelessKnowledgeSession;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
import org.springframework.core.io.support.ResourcePatternResolver;
import org.springframework.core.type.classreading.CachingMetadataReaderFactory;
import org.springframework.core.type.classreading.MetadataReader;
import org.springframework.core.type.classreading.MetadataReaderFactory;
import org.springframework.util.ClassUtils;
import org.springframework.util.SystemPropertyUtils;

import pe.com.bbva.ge.domain.base.BaseDomain;
import pe.com.bbva.ge.domain.configuracion.Regla;
import pe.com.bbva.ge.negocio.ConfiguracionNegocio;

public class MotorDrools implements Serializable {

	/**
	 * 
	 */
	private static final long serialVersionUID = 1L;

	private static final Logger logger = LogManager.getLogger(MotorDrools.class);

	private KnowledgeBuilder kbuilder;
	private KnowledgeBase kbase;
	private Collection<KnowledgePackage> pkgs;

	private StatelessKnowledgeSession ksession;
	
	private final String nuevaLinea = System.getProperty("line.separator");	
	
	private static final String DOMAIN_PACKAGE = "pe.com.bbva.ge.domain";
	
	
	private ConfiguracionNegocio genericoNegocio;
	
	@Autowired
	public MotorDrools(ConfiguracionNegocio genericoNegocio) {
		this.genericoNegocio = genericoNegocio;
		cargarReglas();		
	}

	public void cargarReglas() {
		kbuilder = KnowledgeBuilderFactory.newKnowledgeBuilder();
		kbase = KnowledgeBaseFactory.newKnowledgeBase();
		StringBuilder strReglas = strReglasAgregarImport();
		
		List<Regla> reglas = genericoNegocio.obtenerTodos(Regla.class);
		
		for (Regla regla : reglas) {			
			strReglas.append("rule \"").append(regla.getCodigo()).append("\"").append(nuevaLinea);
			strReglas.append(regla.getRegla()).append(nuevaLinea);
		}
		
		Resource myResource = ResourceFactory.newReaderResource((Reader) new StringReader(strReglas.toString()));
        kbuilder.add(myResource, ResourceType.DRL);
        
        if ( kbuilder.hasErrors() ) {
        	//throw new DroolsErrorException(kbuilder.getErrors());
        }
        
        //Encendemos el Motor
        pkgs = kbuilder.getKnowledgePackages();

        kbase.addKnowledgePackages( pkgs );

        ksession = kbase.newStatelessKnowledgeSession();     
        
        if(logger.isDebugEnabled())
        {
        	logger.debug(strReglas.toString());
        }
	}	

	private String resolverBasePackage(String basePackage) {
		return ClassUtils.convertClassNameToResourcePath(SystemPropertyUtils
				.resolvePlaceholders(basePackage));
	}	

	public List<String> ejecutarReglas(List<BaseDomain> objetos, List<Regla> reglas) {
		
		ComandoDrools comando = new ComandoDrools(objetos, reglas);
		
		ksession.execute(comando);
		
		return comando.getReglasEjecutadas();
	}

	public boolean validarRegla(Regla regla) {
		final KnowledgeBuilder kbuilder = KnowledgeBuilderFactory.newKnowledgeBuilder();
		
		StringBuilder strReglas = strReglasAgregarImport();
		
		strReglas.append("rule \"").append(regla.getCodigo()).append("\"").append(nuevaLinea);
		strReglas.append(regla.getRegla()).append(nuevaLinea);
		
		Resource myResource = ResourceFactory.newReaderResource((Reader) new StringReader(strReglas.toString()));
        kbuilder.add(myResource, ResourceType.DRL);
        
        if ( kbuilder.hasErrors() ) {
        	return false;
        }
        return true;
	}
	
	private StringBuilder strReglasAgregarImport()
	{
		StringBuilder strReglas = new StringBuilder();	
		
		List<Class<?>> clases = buscarClases(DOMAIN_PACKAGE);
		for (Class<?> clase : clases) {
			if(!clase.getName().contains("$") && !clase.getName().contains("quartz"))
			{
				strReglas.append("import ").append(clase.getName()).append(";").append(nuevaLinea);
			}
		}
		strReglas.append(nuevaLinea);
		return strReglas;
	}
	
	private List<Class<?>> buscarClases(String paquete) {
		ResourcePatternResolver resourcePatternResolver = new PathMatchingResourcePatternResolver();
		MetadataReaderFactory metadataReaderFactory = new CachingMetadataReaderFactory(
				resourcePatternResolver);

		List<Class<?>> candidates = new ArrayList<Class<?>>();
		String packageSearchPath = ResourcePatternResolver.CLASSPATH_ALL_URL_PREFIX
				+ resolverBasePackage(paquete) + "/" + "**/*.class";
		org.springframework.core.io.Resource[] resources = null;
		try {
			resources = resourcePatternResolver.getResources(packageSearchPath);
		} catch (IOException e) {
			//throw new InesperadoException(Constante.CODIGO_MENSAJE.ERROR_CARGAR_CLASES, new Object[] {paquete }, e);
		}
		for (org.springframework.core.io.Resource resource : resources) {
			if (resource.isReadable()) {
				MetadataReader metadataReader = null;
				try {
					metadataReader = metadataReaderFactory
							.getMetadataReader(resource);
				} catch (IOException e) {
					//throw new InesperadoException(Constante.CODIGO_MENSAJE.ERROR_CARGAR_CLASES, new Object[] { paquete },	e);
				}
				try {
					candidates.add(Class.forName(metadataReader
							.getClassMetadata().getClassName()));
				} catch (ClassNotFoundException e) {
					//throw new InesperadoException(Constante.CODIGO_MENSAJE.ERROR_CARGAR_CLASES, new Object[] { paquete }, e);
				}
			}
		}
		return candidates;
	}

}
